package com.swg.myspring.util;

import com.swg.myspring.annotation.SwgResource;
import com.swg.myspring.bean.BeanDefinition2;
import com.swg.myspring.bean.PropertyDefinition;
import org.apache.commons.beanutils.ConvertUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class SwgClassPathXmlApplicationContext3 {
    private List<BeanDefinition2> beanDefines=new ArrayList<>();//用来存储所有的beans
    private Map<String, Object> sigletons =new HashMap<>();//用来存储实例化后的bean

    public SwgClassPathXmlApplicationContext3(String fileName){
        this.readXml(fileName);
        this.instanceBeans();
        //3.注解方式注入依赖对象
        this.annotationInject();
    }

    private void annotationInject() {
        //遍历所有的bean
        for (String beanName : sigletons.keySet()) {
            Object bean=sigletons.get(beanName);//获取需要注入的bean
            if (bean != null) {
                try {
                    //先对属性进行处理，即setter方法上标识有注解的
                    BeanInfo info = Introspector.getBeanInfo(bean.getClass());//通过类Introspector的getBeanInfo方法获取对象的BeanInfo 信息
                    PropertyDescriptor[] pds = info.getPropertyDescriptors();//获得 bean所有的属性描述
                    for (PropertyDescriptor pd : pds) {
                        Method setter=pd.getWriteMethod();//获取属性的setter方法
                        //属性存在setter方法，并且setter方法存在YhdResource注解
                        if (setter != null && setter.isAnnotationPresent(SwgResource.class)) {
                            SwgResource resource=setter.getAnnotation(SwgResource.class);//取得setter方法的注解
                            Object value=null;
                            //注解的name属性不为空
                            if (resource != null && resource.name() != null && !"".equals(resource.name())) {
                                value=sigletons.get(resource.name());//根据注解的name属性从容器中取出来
                            }else {//注解上没有标注name属性
                                value=sigletons.get(pd.getName());//根据属性的名称取集合中寻找此名称的bean
                                if (value == null) {
                                    //没找到，遍历所有所有的bean，找类型相匹配的bean
                                    for (String key : sigletons.keySet()) {
                                        //判断类型是否匹配
                                        if (pd.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())) {
                                            value=sigletons.get(key);//类型匹配的话就把此相同类型的
                                            break;//找到了类型相同的bean，退出循环
                                        }
                                    }
                                }
                            }
                            setter.setAccessible(true);//保证setter方法可以访问私有
                            try {
                                setter.invoke(bean,value);//把引用对象注入到属性中
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    //再对字段进行处理，即对字段上标识有注解
                    Field[] fields=bean.getClass().getDeclaredFields();//取得声明的所有字段
                    for (Field field : fields) {
                        //判断字段上是否存在注解,若存在
                        if (field.isAnnotationPresent(SwgResource.class)) {
                            SwgResource resource=field.getAnnotation(SwgResource.class);//取得字段上的注解
                            Object value=null;
                            //字段上存在注解，并且字段上注解的name属性不为空
                            if (resource != null && resource.name() != null && !resource.name().equals("")) {
                                value=sigletons.get(resource.name());//依赖对象为根据此注解的name属性指定的对象
                            }else {
                                value=sigletons.get(field.getName());//根据字段的名称到容器中寻找bean
                                if (value == null) {
                                    //没找到，根据字段的类型去寻找
                                    for (String key : sigletons.keySet()) {
                                        //判断类型是否匹配
                                        if (field.getType().isAssignableFrom(sigletons.get(key).getClass())) {
                                            value=sigletons.get(key);//类型匹配的话就把此相同类型的
                                            break;//找到了类型相同的bean，退出循环
                                        }
                                    }
                                }
                            }
                            field.setAccessible(true);//设置允许访问私有字段
                            try {
                                field.set(bean, value);//将值为value的注入到bean对象上
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                        }
                    }
                } catch (IntrospectionException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    private void readXml(String fileName) {
        //创建一个读取器
        SAXReader saxReader=new SAXReader();
        Document document=null;
        try {
            //获取要读取的配置文件的路径
            URL xmlPath=this.getClass().getClassLoader().getResource(fileName);
            //读取文件内容
            document=saxReader.read(xmlPath);
            //1.获取xml中的根元素
            Element rootElement=document.getRootElement();
            for (Iterator iterator = rootElement.elementIterator(); iterator.hasNext();) {
                Element element = (Element) iterator.next();
                String id=element.attributeValue("id");//获取bean的id属性值
                String clazz=element.attributeValue("class");//获取bean的class属性值
                BeanDefinition2 beanDefinition=new BeanDefinition2(id,clazz);
                //2.获取bean的Property属性
                for (Iterator subElementIterator = element.elementIterator(); subElementIterator.hasNext();) {
                    Element subElement = (Element) subElementIterator.next();
                    String propertyName=subElement.attributeValue("name");
                    String propertyRef= subElement.attributeValue("ref");
                    String propertyValue=subElement.attributeValue("value");
                    //3.构造一个propertyDefinition对象,并且添加进List属性中
                    PropertyDefinition propertyDefinition=new PropertyDefinition(propertyName, propertyRef,propertyValue);
                    beanDefinition.getPropertyDefinitions().add(propertyDefinition);
                }
                beanDefines.add(beanDefinition);
            }
        } catch (Exception e) {
            System.out.println("解析xml失败");
        }
    }

    //这里实例化的功能只是实例化PersonDaoImpl和PersonServiceImpl两个对象，注入的关系还需要下面的函数来解决
    private void instanceBeans(){
        if(beanDefines!=null && beanDefines.size()>0){
            for(BeanDefinition2 beanDefinition:beanDefines){
                try {
                    if (beanDefinition.getClassName() != null && !beanDefinition.getClassName().equals("")) {
                        sigletons.put(beanDefinition.getId(),Class.forName(beanDefinition.getClassName()).newInstance());
                        System.out.println("id为："+beanDefinition.getId()+"的bean实例化成功");
                    }
                }catch (Exception e){
                    System.out.println("bean实例化失败");
                }
            }
        }
    }

    public Object getBean(String beanName,Class beanClass){
        return sigletons.get(beanName);
    }



}
